#include "stdafx.h"
#include "VertexFormat.h"

namespace Cvekas {

VertexFormat::VertexFormat()
	: 
has_position(false), 
number_of_uvs(0), 
has_normal(false), 
has_binormal(false),
has_tangent(false), 
has_color(false), 
needs_rebuild(false), 
declaration(NULL)
{
}

VertexFormat::VertexFormat(bool position, uint UVs, bool normal, bool binormal, bool tangent, bool color)
	: 
has_position(position),
number_of_uvs(UVs),
has_normal(normal),
has_binormal(binormal),
has_tangent(tangent),
has_color(color),
needs_rebuild(true),
declaration(NULL)
{
}

VertexFormat::~VertexFormat()
{
	safeRelease(declaration);
}

uint VertexFormat::getSize() const
{
	int size = 0;
	size += has_position ? 12 : 0;
	size += number_of_uvs * 8;
	size += has_normal ? 12 : 0;
	size += has_binormal ? 12 : 0;
	size += has_tangent ? 12 : 0;
	size += has_color ? 4 : 0;
	return size;
}

uint VertexFormat::getNumberOfElements() const
{
	int n = 0;
	if(has_position) n++;
	n += number_of_uvs;
	if(has_normal) n++;
	if(has_binormal) n++;
	if(has_tangent) n++;
	if(has_color) n++;
	return n;
}


IDirect3DVertexDeclaration9* VertexFormat::getD3DVertexDeclaration(D3DDevicePtr device, uint stream)
{
	if(last_device != device)
	{
		needs_rebuild = true;
		last_device = device;
	}

	if(needs_rebuild)
	{
		safeRelease(declaration);
		D3DVERTEXELEMENT9* elements = new D3DVERTEXELEMENT9[getNumberOfElements() + 1];
		int idx = 0;
		int pos = 0;
		
		if(has_position)
		{
			elements[idx].Stream = stream;
			elements[idx].Offset = pos;
			elements[idx].Type = D3DDECLTYPE_FLOAT3;
			elements[idx].Method = D3DDECLMETHOD_DEFAULT;
			elements[idx].Usage = D3DDECLUSAGE_POSITION;
			elements[idx].UsageIndex = 0;
			idx++;
			pos += 12;
		}
		else
		{
			LOG("Vertex declaration has no position. Is this intentional?");
		}

		if(has_normal)
		{
			elements[idx].Stream = stream;
			elements[idx].Offset = pos;
			elements[idx].Type = D3DDECLTYPE_FLOAT3;
			elements[idx].Method = D3DDECLMETHOD_DEFAULT;
			elements[idx].Usage = D3DDECLUSAGE_NORMAL;
			elements[idx].UsageIndex = 0;
			idx++;
			pos += 12;
		}

		if(has_binormal)
		{
			elements[idx].Stream = stream;
			elements[idx].Offset = pos;
			elements[idx].Type = D3DDECLTYPE_FLOAT3;
			elements[idx].Method = D3DDECLMETHOD_DEFAULT;
			elements[idx].Usage = D3DDECLUSAGE_BINORMAL;
			elements[idx].UsageIndex = 0;
			idx++;
			pos += 12;
		}

		if(has_tangent)
		{
			elements[idx].Stream = stream;
			elements[idx].Offset = pos;
			elements[idx].Type = D3DDECLTYPE_FLOAT3;
			elements[idx].Method = D3DDECLMETHOD_DEFAULT;
			elements[idx].Usage = D3DDECLUSAGE_TANGENT;
			elements[idx].UsageIndex = 0;
			idx++;
			pos += 12;
		}

		if(number_of_uvs > 0 && number_of_uvs <= 8)
		{
			for(uint i = 0; i < number_of_uvs; ++i)
			{
				elements[idx].Stream = stream;
				elements[idx].Offset = pos;
				elements[idx].Type = D3DDECLTYPE_FLOAT2;
				elements[idx].Method = D3DDECLMETHOD_DEFAULT;
				elements[idx].Usage = D3DDECLUSAGE_TEXCOORD;
				elements[idx].UsageIndex = i;
				idx++;
				pos += 8;
			}
		}
		else
		{
			if(number_of_uvs != 0)
				LOG("Number of UVs must be between 0 and 8");
		}

		if(has_color)
		{
			elements[idx].Stream = stream;
			elements[idx].Offset = pos;
			elements[idx].Type = D3DDECLTYPE_D3DCOLOR;
			elements[idx].Method = D3DDECLMETHOD_DEFAULT;
			elements[idx].Usage = D3DDECLUSAGE_COLOR;
			elements[idx].UsageIndex = 0;
			idx++;
			pos += 4;
		}

		elements[idx].Stream = 0xff;
		elements[idx].Offset = 0;
		elements[idx].Type = D3DDECLTYPE_UNUSED;
		elements[idx].Method = 0;
		elements[idx].Usage = 0;
		elements[idx].UsageIndex = 0;

		E_E(device->CreateVertexDeclaration(elements, &declaration));

		delete[] elements;
		needs_rebuild = false;
	}
	return declaration;
}

} // namespace